Skip to content

feat: allow conversational agents to optionally end exchange [JAR-9933]#127

Open
andrewwan-uipath wants to merge 5 commits into
mainfrom
feat/cas-allow-optionally-end-exchange
Open

feat: allow conversational agents to optionally end exchange [JAR-9933]#127
andrewwan-uipath wants to merge 5 commits into
mainfrom
feat/cas-allow-optionally-end-exchange

Conversation

@andrewwan-uipath

@andrewwan-uipath andrewwan-uipath commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for leaving CAS exchanges open after a successful conversational run by introducing an end_exchange flag, while preserving the existing default behavior (emit exchange-end).

Changes:

  • Add end_exchange: bool = True to UiPathChatRuntime and conditionally emit the exchange-end event only when enabled.
  • Map fpsProperties["conversationalService.endExchange"] onto UiPathRuntimeContext.end_exchange with a default of True.
  • Add unit tests for the new mapping and for default/explicit end_exchange behavior; bump package version to 0.12.0.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
uv.lock Updates lock metadata and reflects version bump.
pyproject.toml Bumps package version to 0.12.0.
src/uipath/runtime/context.py Adds end_exchange to context and maps CAS fpsProperty to it.
src/uipath/runtime/chat/runtime.py Adds end_exchange option and guards exchange-end event emission.
tests/test_context.py Adds tests verifying fpsProperty mapping and default behavior for end_exchange.
tests/test_chat.py Adds tests covering default, explicit True, and False behavior for exchange-end emission.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@andrewwan-uipath andrewwan-uipath requested a review from maxduu June 16, 2026 20:19
Comment thread src/uipath/runtime/chat/runtime.py Outdated
self,
delegate: UiPathRuntimeProtocol,
chat_bridge: UiPathChatProtocol,
end_exchange: bool = True,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't add this here - the event needs to be emitted, whether you honor it or not, that should be in the bridge implementation

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andrewwan-uipath uipath-runtime is a very low-level abstraction. CAS/Maestro-specific logic belongs in the implementation layer - it shouldn't be pushed down into the underlying abstractions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, I will move this to the implementation layer

@maxduu maxduu Jun 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cristipufu [Not-blocking] But wanted to ask:
My intuition was leaning towards the original approach Andrew had as well; can you elaborate on what you mean by "the exchange-event needs to be emitted" always? To me, emit_exchange_end_event() is already CAS specific - its a CAS-defined event that says "emit the event that marks the end of the current turn/exchange".

This new feature is to ensure the agent-runtime doesn't always end the exchange (so downstream agents/messages that have more messages to add can end the exchange themselves). This means that for Flow, we can call a conversational agent without having it emit end-exchange event, so that the chat-UI still shows "responding..." rather than that the turn was over.

so my intuition is saying to use the original approach where the runtime-layer doesn't call the emit_exchange_end_event function depending on if a flag is passed-in. Also was thinking it could avoid the weirdness that the function called emit_exchange_end_event but the bridge-implementation doesn't actually send the event conditionally.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now the runtime's event contract changes based on a constructor arg, sometimes it ends the exchange, sometimes it doesn't, so you can't reason about what events come out of the runtime without knowing how it was built. The "don't end the turn so the UI keeps showing responding…" logic is pure CAS/Flow orchestration concern, and we're pushing it down into a low-level abstraction that shouldn't know Flow exists.
The runtime calling emit_exchange_end_event() unconditionally and the bridge deciding what that means isn't weird, it's the layering working: runtime reports "my turn is done," bridge owns the CAS semantics of whether that translates to a terminal event on the wire. If Flow wants to suppress it, the bridge is the thing that knows that. The runtime doesn't.
So same outcome you want, Flow can keep the exchange open, just with the branch living in the layer that already owns the CAS protocol rather than as a flag on the generic runtime.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, thank you @cristipufu ! I guess its more of a naming thing then - emit_exchange_end_event should probably be changed to emit_turn_complete_event but its not a big issue and totally okay in my opinion.

I'm good to get this method (extracted to bridge layer) merged.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread pyproject.toml Outdated
[project]
name = "uipath-runtime"
version = "0.11.0"
version = "0.12.0"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can get away with a patch increase to avoid incrementing the ranges in all the repos

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants